# -*- coding: iso-8859-2 -*-

import re
import mdp
import numpy
import mlpy
import math
import os
import datetime
import random
import sys

from authorshipanalyzer.authorslot import AuthorSlot
from authorshipanalyzer.utils import xuniqueCombinations
from authorshipanalyzer.file import Path

class Classifier:
    '''
    Classify author slots
    '''
    
    def __init__(self, selector, kernel='linear', kp=0.10000000000000001, C=1.0, tol=0.001, eps=0.001, maxloops=1000, cost=0.0):
        '''
        @param selector: dictionary managing feature vector creation
        SVM params:
        @param kernel    - 'linear', 'gaussian', 'polynomial', 'tr'
        @param kp        - kernel parameter (two sigma squared) for gaussian and polynomial kernel
        @param C         - regularization parameter
        @param tol       - tolerance for testing KKT conditions
        @param eps       - convergence parameter
        @param maxloops  - maximum number of optimization loops
        @param cost      - for cost-sensitive classification [-1.0, 1.0]
        '''
        self.selector = selector
        self.classifier = mlpy.svm(kernel, kp, C, tol, eps, maxloops, cost)          
    
    def classify(self, unknownAuthorSlot, knownAuthorSlots):
        '''
        Classify
        @return dict<author, comparison_result_in_percent>
        '''
        
        # feature vector of unknown author
        uVector = unknownAuthorSlot.getFeaturesVector(self.selector, unknownAuthorSlot)
        
        # feature vectors of known authors
        kVectors = []

        # unknown author feature vector in the first line, known in the following
        allVectors = [ uVector ]
        
        # vector creation
        for knownAuthorSlot in knownAuthorSlots:
            kVector = knownAuthorSlot.getFeaturesVector(self.selector, unknownAuthorSlot)
            kVectors.append(kVector)
            allVectors.append(kVector)
        
        # voting array
        voteArray = numpy.zeros( len(knownAuthorSlots) )
        
        # generate pairs of slots
        for pair in xuniqueCombinations(range(len(knownAuthorSlots)), 2): 
            # compute svm
            self.classifier.compute( \
                                     numpy.array([ kVectors[pair[0]], kVectors[pair[1]] ]), \
                                     numpy.array([ pair[0] + 1, pair[1] + 1]))     
            # predict svm model on test point    
            selectedClass = self.classifier.predict(numpy.array(uVector)) - 1
            
            # if classified, vote to winner
            if selectedClass > -1:
                voteArray[selectedClass] += 1  
        
        result = {}
        divider = float(len(knownAuthorSlots) - 1)
        if len(knownAuthorSlots) < 2:
            result[knownAuthorSlots[0].author] = 100.0
            print "%35s = %3.2f%%" % (knownAuthorSlots[0].author, result[knownAuthorSlots[0].author])
        else:
            for i in range(len(knownAuthorSlots)):
                result[knownAuthorSlots[i].author] = float(voteArray[i] * 100) / divider
                print "%35s = %3.2f%%" % (knownAuthorSlots[i].author, result[knownAuthorSlots[i].author])
            
        # print PCA result
        self.pcaResult(allVectors, knownAuthorSlots, unknownAuthorSlot.author, 2)
   
        return result
    
    def pcaResult(self, allVectors, knownAuthorSlots, unknownAuthor, dim = 2):
        '''
        Print PCA result
        '''
        fAllVectors = numpy.array(allVectors)
        pcanode = mdp.nodes.PCANode(len(allVectors[0]), 2, 'float64', True)
        pcanode.train(fAllVectors)
        pcanode.stop_training()
        ypca = pcanode.execute(fAllVectors, 2)
        
        xrange = [-1.0, 1.0]
        yrange = [-1.0, 1.0]
        for i in range(0, len(knownAuthorSlots) + 1):
            if ypca[i, 0] < xrange[0]:
                xrange[0] = ypca[i, 0]
            if ypca[i, 0] > xrange[1]:
                xrange[1] = ypca[i, 0]
            if ypca[i, 1] < yrange[0]:
                yrange[0] = ypca[i, 1]
            if ypca[i, 1] > yrange[1]:
                yrange[1] = ypca[i, 1]               
        
        print str(ypca[0, 0]) + " " + str(ypca[0, 1]) + " " + self.createLabel(unknownAuthor)
        for i in range(1, len(knownAuthorSlots) + 1):
            for j in range(2):
                print str(ypca[i, j]) + " ",
            print self.createLabel(knownAuthorSlots[i - 1].author)
            
        # create output directory
        d = datetime.datetime.now()
        outputDir = "output_%s_%d" % ( d.strftime("%y%m%d_%H.%M.%S"), random.randint(0, sys.maxint) )
        outputDir = os.path.join(Path.getResourceDir(), "pca", outputDir)
        os.mkdir(outputDir)
        
        # output file with unknown slot
        uOutput = os.path.join(outputDir, "unknown.dat")
        uOutputFile = file(uOutput, "w")
        uOutputFile.write(str(ypca[0, 0]) + " " + str(ypca[0, 1]) + " " + self.createLabel(unknownAuthor))
        uOutputFile.write("\n")
        uOutputFile.flush()
        uOutputFile.close()
        
        # known searched author
        skOutput = os.path.join(outputDir, "sknown.dat")
        
        # output file with known slots
        kOutput = os.path.join(outputDir, "known.dat")
        kOutputFile = file(kOutput, "w")
        for i in range(1, len(knownAuthorSlots) + 1):
            for j in range(2):
                kOutputFile.write(str(ypca[i, j]) + " ")
            if knownAuthorSlots[i - 1].author == unknownAuthor:
                skOutputFile = file(skOutput, "w")
                skOutputFile.write(str(ypca[i, 0]) + " ")
                skOutputFile.write(str(ypca[i, 1]) + " ")                   
                skOutputFile.write(self.createLabel(knownAuthorSlots[i - 1].author))
                skOutputFile.write("\n")
                skOutputFile.flush()
                skOutputFile.close()
            kOutputFile.write("\n")
        kOutputFile.flush()
        kOutputFile.close()
        
        # generate gnuplot script for data plotting
        gOutput = os.path.join(outputDir, "plot_pca.gpl")
        gOutputFile = file(gOutput, "w")
        gOutputFile.write("set title 'Author slot distribution using PCA visualization in 2D'")
        gOutputFile.write("\n")           
        gOutputFile.write("set xrange [%s:%s]" % (xrange[0] - 0.1, xrange[1] + 0.1))
        gOutputFile.write("\n")
        gOutputFile.write("set yrange [%s:%s]" % (yrange[0] - 0.1, yrange[1] + 0.1))
        gOutputFile.write("\n")
#        gOutputFile.write("set lmargin 50\n")
#        gOutputFile.write("set rmargin 50\n")
        gOutputFile.write("set key box\n")
        gOutputFile.write("set key outside\n")
        gOutputFile.write("set key below\n")
        gOutputFile.write("set terminal png size 1280,1024\n")
        outputPng = os.path.join(outputDir, "output.png")
        gOutputFile.write("set output '%s'\n" % outputPng)
        
        cmd = "plot '%s' using 1:2 with points pt 6 lt rgb \"red\" title \"Known Author Slot\", " + \
            "'%s' using 1:2 with points pt 6 lt rgb \"violet\" title \"Searched Known Author Slot\", " + \
            "'%s' u 1:2:3 w labels left point pt 0 offset 1,0 notitle, " + \
            "'%s' using 1:2 with points pt 6 lt rgb \"blue\" title \"Unknown Author Slot\", " + \
            "'%s' u 1:2:3 w labels left point pt 0 offset 1,0 notitle"
        
        gOutputFile.write(cmd % (kOutput, skOutput, skOutput, uOutput, uOutput))
        gOutputFile.write("\n")
        gOutputFile.flush()
        gOutputFile.close()
        
        os.system("gnuplot " + gOutput)
        
    
    def createLabel(self, author):
        return re.sub("\s+", "_", author)